import random;

class Player:
    def __init__(self):
        self.total_hunts_per_player = 0

    # Strategy: Identify each player from last round and play tit for tat.
    def hunt_choices(self, round_number, current_food, current_reputation, m,
            player_reputations):

        # Sort reputations and keep a mapping back to the original indices
        sorted_kv_pairs = sorted(enumerate(player_reputations), key=lambda x:x[1])
        sorted_reputations = [ x[1] for x in sorted_kv_pairs ]
        sorted_idx_to_curr = [ x[0] for x in sorted_kv_pairs ]

        # Work with total number of cooperations rather than reputation
        current_coops = [ x * self.total_hunts_per_player for x in sorted_reputations ]

        if (round_number == 1):
            hunt_decisions = [ 'h' for x in player_reputations ]
        else:
            previous_coops = self.previous_coops
            whitelists = self.generate_whitelists(current_coops, previous_coops)
            probabilities = self.convert_whitelists_to_probabilities(current_coops, previous_coops, whitelists)
            hunt_decisions = self.map_decisions(probabilities, whitelists, self.sorted_idx_to_prev)

        self.previous_coops = current_coops
        self.total_hunts_per_player += len(player_reputations) - 1
        self.sorted_idx_to_prev = sorted_idx_to_curr
        return hunt_decisions

    # Finds the possible matchings from the reputations array to the previous decision array.
    # Returns a list of lists: for each current reputation, get the list of pointers (indices) to decisions that possibly match.
    # Strategy: Find the obvious 1 to 1 matches. Then scan for groups that must be matched together.
    def generate_whitelists(self, current_coops, previous_coops):
        max_coop_last_round_per_player = len(previous_coops) - 1

        whitelists = [ [] for x in current_coops ]
        for i in range(len(current_coops)):
            for j in range(len(previous_coops)):
                if abs(current_coops[i] - previous_coops[j]) <= max_coop_last_round_per_player:
                    whitelists[i] = whitelists[i] + [j]

        def process_group(i, group_size):
            # If each member of the group has the same whitelist and it's the same size of the group,
            # no other current reputations could match any decisions in the whitelist
            # so remove the whitelist elements from all other whitelists

            first_whitelist = whitelists[i]
            if len(first_whitelist) != group_size:
                return False

            for j in range(i + 1, i + group_size):
                if whitelists[j] != first_whitelist:
                    return False

            # Remove whitelist from other whitelists
            for j in range(len(whitelists)):
                if j >= i and j < i + group_size: continue
                whitelists[j] = [ x for x in whitelists[j] if x not in first_whitelist ]

            return True

        group_size = 0
        matched_groups = []
        while group_size < len(current_coops) / 2:
            group_size += 1
            i = 0
            for i in range(len(current_coops) - group_size):
                if [i, group_size] in matched_groups: continue;
                if process_group(i, group_size):
                    # Whitelists have been updated so start over
                    matched_groups = matched_groups + [ [i, group_size] ]
                    group_size = 0
                    break

        return whitelists

    # Computes the probability that a given reputation matches a given decision.
    # Returns an array similar to the whitelist, but with probabilities instead of pointers.
    # Strategy: A decision is more likely to match with a reputation if the previous reputation is closer to the
    # current reputation. Use this distance as a weight for each possibility.
    def convert_whitelists_to_probabilities(self, current_coops, previous_coops, whitelists):
        max_coop_last_round_per_player = len(previous_coops) - 1
        probabilities = [ [] for x in whitelists ]
        for i in range(len(whitelists)):
            weights = []
            for j in whitelists[i]:
                weights = weights + [2 - abs(previous_coops[j] - current_coops[i]) / float(max_coop_last_round_per_player) ]

            probabilities[i] = [ x / sum(weights) for x in weights ]

        return probabilities

    # Maps reputations to decisions by executing the calculated probabilities
    def map_decisions(self, probabilities, whitelists, sorted_idx_to_prev):
        decisions = []

        for i in range(len(probabilities)):
            r = random.random()
            a = 0
            for j in range(len(probabilities[i])):
                a += probabilities[i][j]
                if (r < a):
                    prev_enemy_sorted_idx = whitelists[i][j]
                    break

            prev_enemy_decision = self.enemy_prev_decisions[sorted_idx_to_prev[prev_enemy_sorted_idx]]
            decisions = decisions + [prev_enemy_decision]

        return decisions

    # Store enemy decisions made for this round
    def hunt_outcomes(self, food_earnings):
        self.enemy_prev_decisions = []

        for i in range(len(food_earnings)):
            enemy_cooperated = food_earnings[i] >= 0
            enemy_decision = 'h' if enemy_cooperated else 's'
            self.enemy_prev_decisions = self.enemy_prev_decisions + [enemy_decision]

    def round_end(self, award, m, number_hunters):
        pass # do nothing